﻿#include "QtStudy.h"
#include "basic_data_type.h"

/*
* 提供公共方法来触发信号
*/
void TestSignal::triggerSignal()
{
    qDebug() << "Emitting send signal with no args";
    // 发射无参信号
    emit send_signal();
}

void TestSignal::triggerSignal(int x)
{
    qDebug() << "Emitting send signal with arg:" << x;
    // 发射无参信号
    emit send_signal(x);
}

QtStudy::QtStudy(QWidget *parent) : QMainWindow(parent)
{
    // 双向绑定，为UI对象设置对应的窗体
    ui.setupUi(this);
    
    /* 一.Qt基础数据类型 */
    // 1.Qt基本数据类型
    basic_data_type();
    // 2. Qt字符串类型:QByteArray
    qt_qbytearray();
    // 3. Qt字符串类型:QString
    qt_qstring();
    // 4. QVariant
    test_qvariant();

    /* 二.信号槽 */
    // 1.Qt5后的语法，使用函数指针。这种方法在Qt6中仍被推荐
    connect(ui.pushButton, &QPushButton::clicked, this, &QMainWindow::close);
    // 2.Qt4的语法，字符串语法。这种方法在Qt5后不再被推荐
    // connect(ui.pushButton, SIGNAL(clicked()), this, SLOT(close()));
    // 3.lamuda表达式语法，结合C++11语法新特性
    // 正确:槽函数（回调函数）可以使用拉姆达表达式（实际是匿名函数）
    // connect(ui.pushButton, &QPushButton::clicked, this, [this]() {
    //         this->close();
    // });
    // 错误，原因是：信号是类接口的一部分，需要在类定义中明确，不能使用拉姆达表达式。且此处手动触发按钮按下，逻辑也是错误的。
    //connect(ui.pushButton, [this]() {
    //    ui.pushButton->clicked(); }, this, [this]() {
    //        this->close();
    //        });
    // 错误，原因是：信号是类接口的一部分，需要在类定义中明确，不能使用拉姆达表达式。且此处手动触发按钮按下，逻辑也是错误的。
    //QPushButton* btn = ui.pushButton;
    //connect(ui.pushButton, [btn]() {
    //    btn->clicked(); }, this, [this]() {
    //    this->close(); 
    // });
    // 4.使用QObject::connect的返回值：Qt6 中 connect() 返回 QMetaObject::Connection，可用于断开连接
    // QMetaObject::Connection connection = connect(ui.pushButton, &QPushButton::clicked, this, &QMainWindow::close);
    // disconnect(connection);
    // 5.利用自动连接命名约定：如果槽函数命名为 on_对象名_信号名，Qt 会自动连接

    // 6.自定义信号槽与标准信号绑定
    connect(ui.pushButton_2, &QPushButton::clicked, this, &QtStudy::on_pushButton_2_clicked);

    // 7.自定义信号和槽函数
    // 7.1创建信号源对象
    TestSignal ts;
    // 7.2.1针对信号槽重载的情况，Qt4版本的绑定信号函数和槽函数：字符串连接。正确
    // connect(&ts, SIGNAL(send_signal()), this, SLOT (get_message()));
    // connect(&ts, SIGNAL(send_signal(int)), this, SLOT(get_message(int)));
    // 7.2.2针对信号槽重载的情况，若直接Qt4版本的绑定信号函数和槽函数：函数指针，则会出错。因为不知道指向
    // connect(&ts, &TestSignal::send_signal, this, &QtStudy::get_message);
    // 不过可以手动定义对应类型的函数指针，给connect传参
    void (TestSignal:: *send_signal_with_no_arg)() = &TestSignal::send_signal;
    void (QtStudy:: *get_message_with_no_arg)() = &QtStudy::get_message;
    connect(&ts, send_signal_with_no_arg, this, get_message_with_no_arg);
    void (TestSignal:: * send_signal_with_arg)(int) = &TestSignal::send_signal;
    void (QtStudy:: * get_message_with_arg)(int) = &QtStudy::get_message;
    connect(&ts, send_signal_with_arg, this, get_message_with_arg);
    // 7.3调用信号函数
    // 无参
    ts.triggerSignal();
    // 有参
    ts.triggerSignal(9);

    /* 三.定时器 */
    // 1.设置、读取时间精度
    qtimer.setTimerType(Qt::PreciseTimer);
    Qt::TimerType timer_type = qtimer.timerType();
    qDebug() << "时间精度: " << timer_type;
    // 2.设置、读取时间间隔
	qtimer.setInterval(1);
    int interval = qtimer.interval();
    qDebug() << "时间间隔: " << interval;
    // 3.设置定时器只触发一次
    // qtimer.setSingleShot(true);
    // 判断定时器是否只触发一次
    bool single_mark = qtimer.isSingleShot();
    qDebug() << "是否只执行一次: " << interval;
    // 4.启动或重启定时器，若无参数传入，则需通过setInterval设置
    qtimer.start();
    // 启动或重启定时器，若有参数传入，则无需通过setInterval设置
    // qtimer.start(1);
    // 5.判断定时器是否正在运行
    bool active_mark = qtimer.isActive();
    qDebug() << "是否运行: " << active_mark;
    // 6.停止定时器
    // qtimer.stop();
    // 7.绑定定时器
    connect(&qtimer, &QTimer::timeout, this, &QtStudy::update_current_time);
    // 8.定时器的另一种用法：static pubic 静态公共方法，定时器仅执行一次
    // [static] void QTimer::singleShot(
    //     int msec, const QObject * receiver,
    //     PointerToMemberFunction method);
    QTimer::singleShot(1, this, []() {
        qDebug() << "静态单次定时器被执行";
    });

    /* 四.窗口 */
    // 1.widgit
    // 1.1设置父对象
    // void QWidget::setParent(QWidget *parent);
    // void QWidget::setParent(QWidget* parent, Qt::WindowFlags f);
    sub_window.setParent(this);
    // 1.2获取窗口的父对象
    QWidget* sub_parent = sub_window.parentWidget();
    qDebug() << sub_parent;
    // 1.3设置当前窗口的几何信息(位置和尺寸信息), 不包括边框
    sub_window.setGeometry(0,300,50,50);
    // 1.4获取当前窗口相对于父窗口的位置（包括边框）
    QRect r_no_edge = sub_window.frameGeometry();
    qDebug() << r_no_edge;
    // 1.t获取当前窗口相对于父窗口的位置（不包括边框）
    QRect r_with_edge = sub_window.geometry();
    qDebug() << r_with_edge;
    // 1.6设置窗体背景色（样式表）
    sub_window.setStyleSheet("background-color: red;");
    // 1.7显示窗口
    sub_window.show();
    // 1.8移动窗口位置
    connect(ui.pushButton_3, &QPushButton::clicked, this, [this]() {
        if (sub_window.frameGeometry().x() + 10 > this->frameGeometry().x()) 
        {
            sub_window.move(0, sub_window.frameGeometry().y());
        }
        else 
        {
            sub_window.move(sub_window.frameGeometry().x() + 10, sub_window.frameGeometry().y());
        }
        
    });
    // 1.9获取、设置窗口的尺寸信息
    QSize qs = sub_window.size();
    qDebug() << qs;
    sub_window.resize(50,50);
    // 1.10 获取、设置窗口的固定尺寸
    // 1.11 获取、设置窗口的最大尺寸
    // 1.12 获取、设置窗口的最小尺寸
    // 1.13 获取、设置窗口的高度、最小高度、最大高度
    // 1.14 获取、设置窗口的标题
    sub_window.setWindowTitle(QString("子窗口"));
    QString sub_window_tittle = sub_window.windowTitle();
    qDebug() << sub_window_tittle;
    // 1.15 获取、设置窗口的图标
    // 使用.qrc维护项目中的资源文件
    QIcon set_sub_window_icon(":/QtStudy/icon.ico");
    sub_window.setWindowIcon(set_sub_window_icon);
    QIcon get_sub_window_icon = sub_window.windowIcon();
    qDebug() << get_sub_window_icon;

    // 2.QMainWindow
    connect(ui.actionClick, &QAction::triggered, this, []()
        {
            qDebug() << "点击菜单项";
     });
    
    // 3.QDialog自定义对话框
    connect(ui.pushButton_4, &QPushButton::clicked, this, [this]() 
    {
            MyDialog my_dialog(this);
            // 模态显示窗口
            int ret = my_dialog.exec();
            if (ret == QDialog::Accepted)
            {
                qDebug() << "Accept: " << ret;

            }
            else if (ret == QDialog::Rejected)
            {
                qDebug() << "Accept: " << ret;
            }
            else
            {
                qDebug() << "Others: " << ret;
            }
    });
}


QtStudy::~QtStudy()
{}

/*
* 按键计数槽函数
*/
void QtStudy::on_pushButton_2_clicked()
{
    
    QString s;
    s = s.fromStdString(std::to_string(this->click_count++));
    qDebug() << s;
    ui.lineEdit->setText(s);
}

/*
* 无参接收信息槽函数
*/
void QtStudy::get_message() 
{
    qDebug() << "get message with no args";
}

/*
* 含参接收信息槽函数
*/
void QtStudy::get_message(int x)
{
    qDebug() << "get message with no arg: " << x;
}

/*
* 更新当前时间
*/
void QtStudy::update_current_time() 
{
    QDateTime qdt = QDateTime::currentDateTime();
    QString s = qdt.toString();
    ui.lineEdit_2->setText(s);
}

/*
* 自定义对话框的初始化函数
*/
MyDialog::MyDialog(QWidget* parent) : QDialog(parent)
{
    this->setParent(parent);
    this->dialog_button1.setParent(this);
    this->dialog_button1.setText("accept");
    this->dialog_button1.setGeometry(50,50,50,30);
    connect(&this->dialog_button1, &QPushButton::clicked, this, [this]() {
        // 隐藏模态窗口, 并且解除模态窗口的阻塞, 将 exec() 的返回值设置为 QDialog::Accepted
        this->accept();
     });
    this->dialog_button1.show();

    this->dialog_button2.setParent(this);
    this->dialog_button2.setText("reject");
    this->dialog_button2.setGeometry(150, 50, 50, 30);
    connect(&this->dialog_button2, &QPushButton::clicked, this, [this]() {
        // 隐藏模态窗口, 并且解除模态窗口的阻塞, 将 exec() 的返回值设置为 QDialog::Rejected
        this->reject();
        });
    this->dialog_button2.show();

    this->dialog_button3.setParent(this);
    this->dialog_button3.setText("done");
    this->dialog_button3.setGeometry(250, 50, 50, 30);
    connect(&this->dialog_button3, &QPushButton::clicked, this, [this]() {
        // 关闭对话框并将其结果代码设置为r。finished()信号将发出r;
        // 如果r是QDialog::Accepted 或 QDialog::Rejected，则还将分别发出accept()或Rejected()信号。
        this->done(9);
        });
    this->dialog_button3.show();

}

/*
* 自定义对话框的析构函数
*/
MyDialog::~MyDialog()
{

}